﻿using System;
using System.Collections.Generic;
using System.Reflection;
using Moq;
using Myotragus.Data.Containers;
using Myotragus.Data.Containers.Windsor;
using Myotragus.Data.Criteria;
using Myotragus.Data.Domain;
using Myotragus.Data.Emit;
using Myotragus.Data.Metadata;
using Myotragus.Data.Registration;
using Myotragus.Data.Repositories;
using Myotragus.Data.Tests.Domain;
using NHibernate.ByteCode.Castle;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using Environment=NHibernate.Cfg.Environment;
using Myotragus.Data.Util;

namespace Myotragus.Data.Tests.Utils
{
	public class FixturesHelper
	{
		public static readonly Type TypeOfObject = typeof(object);
		public static readonly Type TypeOfString = typeof(string);
		public static readonly Type TypeOfTestEntity = typeof(ITestEntity);
		public static readonly Type TypeOfTestEntity2 = typeof(ITestEntity2);
		public static readonly Type TypeOfInt = typeof(int);

		public static INameGenerator CreateNameGenerator()
		{
			return new GuidNameGenerator();
		}

		public static IDynamicAssembly CreateAssemblyMock(string assemblyName, Mock<IDynamicType> typeMock)
		{
			var assemblyMock = new Mock<IDynamicAssembly>();

			assemblyMock.Setup(s => s.MainNamespace).Returns(assemblyName);
			assemblyMock.Setup(s =>
				s.DefineType(It.IsAny<string>(), It.IsAny<TypeAttributes>(), It.IsAny<Type>(), It.IsAny<IEnumerable<Type>>()
			)).Returns((string name, TypeAttributes attr, Type baseType, IEnumerable<Type> interfaces) =>
					InitializeTypeMock(typeMock, name, attr, baseType, interfaces));

			return assemblyMock.Object;
		}

		public static IDynamicType InitializeTypeMock(Mock<IDynamicType> typeMock, string name, TypeAttributes attr,
			Type baseType, IEnumerable<Type> interfaces)
		{
			var interfacesLst = new List<Type>(interfaces);

			typeMock.Setup(m => m.TypeName).Returns(name);
			typeMock.Setup(m => m.Interfaces).Returns(interfacesLst);
			typeMock.Setup(m => m.Attributes).Returns(attr);
			typeMock.Setup(m => m.BaseType).Returns(baseType);

			typeMock.Setup(m => m.DefineAutomaticProperty(It.IsAny<string>(), It.IsAny<Type>()));

			return typeMock.Object;
		}

		public static Configuration CreateFakeDb()
		{
			return new Configuration()
				.SetProperty(Environment.ReleaseConnections, "on_close")
				.SetProperty(Environment.Dialect, typeof(SQLiteDialect).AssemblyQualifiedName)
				.SetProperty(Environment.ConnectionDriver, typeof(SQLite20Driver).AssemblyQualifiedName)
				.SetProperty(Environment.ConnectionString, "data source=:memory:")
				.SetProperty(Environment.ProxyFactoryFactoryClass, typeof(ProxyFactoryFactory).AssemblyQualifiedName)
				.SetProperty(Environment.ShowSql, "true");
		}

		public static IServicesRegistrar RegisterServices()
		{
			var container = new ServicesContainer();

			var all = ServicesImplementation.FromAssemblyContaining<IEntity>();

			container.RegisterForAll(all
				.NotImplementing<IQueryBuilder>()
				.NotImplementing<IRepository>());
			
			container.RegisterForDefault(all.Implementing<IQueryBuilder>());
			container.RegisterForDefault(all.Implementing<IRepository>());

			return container;
		}

		public static Mock<ILocalizableEntityMapping> CreateLocalizableMapping()
		{
			var mapping = new Mock<ILocalizableEntityMapping>();

			return mapping;
		}

		public static Mock<IPrimaryKeyPropertyMapping> CreateId(string name, Type type)
		{
			var mid = new Mock<IPrimaryKeyPropertyMapping>();

			mid.Setup(m => m.PropertyType).Returns(type);
			mid.Setup(m => m.PropertyName).Returns(name);
			mid.Setup(m => m.ColumnName).Returns(name);
			mid.Setup(m => m.Generator).Returns("generated");

			return mid;
		}

		public static Mock<IPrimaryKeyPropertyMapping> CreateId(string name)
		{
			return CreateId(name, TypeOfInt);
		}

		public static Mock<IPrimaryKeyPropertyMapping> CreateId()
		{
			return CreateId("id", TypeOfInt);
		}
	}
}